<template>
  <div class="goods-sku">
    <dl v-for="item in goods?.specs" :key="item.name">
      <dt>{{ item.name }}</dt>
      <dd>
        <template v-for="val in item.values" :key="val.name">
          <img
            v-if="val.picture"
            :src="val.picture"
            alt=""
            :title="val.name"
            @click="changeSelected(item, val)"
            :class="{ selected: val.selected, disabled: val.disabled }"
          />
          <span
            v-else
            @click="changeSelected(item, val)"
            :class="{ selected: val.selected, disabled: val.disabled }"
            >{{ val.name }}</span
          >
        </template>
      </dd>
    </dl>
  </div>
</template>
<script>
import getPowerSet from '@/vender/power-set'
export default {
  name: 'GoodsSku',
  props: {
    goods: Object,
    skuId: {
      type: String,
      default: ''
    }
  },
  setup (props, { emit }) {
    // 规格信息渲染以及点亮功能
    // spec 指每一个规格分类  val指每个规格具体的值
    const changeSelected = (spec, val) => {
      // 这里当按钮禁用后，禁止用户点击按钮，直接return掉
      if (val.disabled) return

      if (val.selected) {
        val.selected = false
      } else {
        // 让这个规格下所有的值先不选中，再让当前点的值选中
        spec.values.forEach(item => {
          item.selected = false
        })
        // 让自己选中
        val.selected = true
      }
      // 改变按钮的状态的时候控制哪个按钮禁用
      updateDisabledStatus(props.goods.specs, pathMap)

      // 子传父,将选中的sku信息传递给父组件
      let selectedValues = getSelectedSpec(props.goods.specs)
      selectedValues = selectedValues.filter(v => v) // 过滤掉undefined
      // 如果选中的规格 没有选全此时不应该更改父组件的价格等信息，所以传递一个空对象
      if (selectedValues.length === props.goods.specs.length) {
        // 如果规格信息选全了，那么就去字典对象查找对应属性路径的值，
        // 里面会有当前选中的规格对应的skuid数组
        const skuIds = pathMap[selectedValues.join('☆')]
        // 根据得到的skuid数组 去获取对应的sku对象
        const sku = props.goods.skus.find(item => item.id === skuIds[0])
        emit('changeSku', sku)
      }
    }

    /* 思路:
      1、根据后台返回的skus数据拿到所有的sku组合，过滤出库存不为0的skus组合
      2、根据有效的skus组合得到每一个sku组合的路径组合,比如一个sku组合包含了
         颜色: 白色  尺码: S   那么这一个sku可能出现的情况就是:
         1.买家只点了白色  2.买家只点了S尺码  3.买家点了白色S尺码 4.买家什么都没点
         这里用到了幂集算法，可以快速求出每一个sku组合出现的所有情况
      3、得到所有有效skus组合得出每一个sku可能出现的路径组合后,生成一个对象字典,
         出现相同组合就往对象的值里面push一个sku的id，没出现过就给对象字典新增一个属性，
         并把sku的id设置为属性值
      4、根据对象字典，我们可以根据用户点击的规格去字典找判断下一个规格的某个按钮是否要禁用,
        如果可走路径在字典中就可以点击，不在字典里就禁用掉
    */


    const pathMap = getPathMap(props.goods.skus) // 得到字典对象
    // console.log(pathMap)

    initSelectedStatus(props.goods, props.skuId)
    // 页面一进来就更新按钮状态
    updateDisabledStatus(props.goods.specs, pathMap)
    return {
      changeSelected
    }
  }
}

// 封装选中的规格的值
/* arr = ['黑色', 'undefined', '10cm'] 类似这样用一个数组去收集每一个规格哪个值选中了,
如果这个规格下没有选中的值，就为undefined */
const getSelectedSpec = (specs) => {
  const arr = []
  // 遍历每一个规格数组
  specs.forEach((spec, index) => {
    // 找到每一个规格数组下 分别哪个值selected为true,比如颜色规格下面 黑色为true
    const val = spec.values.find(item => item.selected)
    if (val) {
      arr[index] = val.name
    } else {
      arr[index] = undefined
    }
  })
  return arr
}
// 封装获取禁用状态的函数
const updateDisabledStatus = (specs, pathMap) => {
  specs.forEach((spec, index) => {
    // 获取选中的规格值的数组
    const selectedValues = getSelectedSpec(specs)
    // console.log(selectedValues, spec)
    spec.values.forEach(item => {
      selectedValues[index] = item.name
      // 过滤掉undefined数组项
      const key = selectedValues.filter(v => v).join('☆')
      // console.log(key)
      item.disabled = !pathMap[key]
    })
  })
}
// 封装得到对象字典的函数
const getPathMap = (skus) => {
  // console.log(skus)
  const pathMap = {} // 定一个存放字典的空对象
  skus.filter(item => item.inventory > 0).forEach(item => {
    // 获取每一个sku用到的规格的值的数组 比如:['白色','s尺码','170cm']
    const specs = item.specs.map(spec => spec.valueName)
    // console.log(specs)
    // 获取每一个sku规格组成的数组的所有路径可能 也就是思路所说的第2步
    const PowerSet = getPowerSet(specs)
    // console.log(PowerSet)
    // 得到的路径数组 数组里面套数组的 这里我们把数组的每一项用五星连接转换成字符串
    PowerSet.forEach(v => {
      const key = v.join('☆')
      // 如果对象里面有这个路径了 就把当前sku的id push到属性值里面，如果没有就新增设置一个
      if (pathMap[key]) {
        pathMap[key].push(item.id)
      } else {
        pathMap[key] = [item.id]
      }
    })
  })
  return pathMap
}

// 封装初始化默认选中状态
const initSelectedStatus = (goods, skuId) => {
  // 根据skuid找到这个商品对应的sku
  const sku = goods.skus.find(item => item.id === skuId)
  // 判断 当sku有的时候 遍历当前商品的specs规格数组，得到这个sku对应的规格信息的值
  // 然后根据得到的当前sku规格信息的值 去找每一个spec规格下的值  判断相等的时候就为true
  // 让他选中
  if (sku) {
    goods.specs.forEach((spec, index) => {
      const value = sku.specs[index].valueName
      spec.values.find(val => val.name === value).selected = true
    })
  }
}
</script>
<style scoped lang="less">
.sku-state-mixin () {
  border: 1px solid #e4e4e4;
  margin-right: 10px;
  cursor: pointer;
  &.selected {
    border-color: @xtxColor;
  }
  &.disabled {
    opacity: 0.6;
    border-style: dashed;
    cursor: not-allowed;
  }
}
.goods-sku {
  padding-left: 10px;
  padding-top: 20px;
  dl {
    display: flex;
    padding-bottom: 20px;
    align-items: center;
    dt {
      width: 50px;
      color: #999;
    }
    dd {
      flex: 1;
      color: #666;
      > img {
        width: 50px;
        height: 50px;
        .sku-state-mixin ();
      }
      > span {
        display: inline-block;
        height: 30px;
        line-height: 28px;
        padding: 0 20px;
        .sku-state-mixin ();
      }
    }
  }
}
</style>
